* Add support for Qt6.
* fix a few codacy complaints.
source ${HOME}/Cache/qt-${QT_VERSION}.env
sudo xcode-select --switch /Applications/Xcode_12.1.1.app
./tools/travis_script_osx
+
+ macos_qt6:
+ name: macos Qt6 Build
+ runs-on: macos-11
+ env:
+ QT_VERSION: '6.2.0'
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v2
+
+ - name: Cache Qt
+ uses: actions/cache@v2
+ id: cache
+ with:
+ path: ~/Cache
+ key: ${{ runner.os }}-${{ env.QT_VERSION }}-${{ secrets.CACHE_VERSION }}
+
+ - name: Qt install setup
+ if: steps.cache.outputs.cache-hit != 'true'
+ uses: actions/setup-python@v2
+ with:
+ python-version: '3.9'
+
+ - name: Qt install
+ if: steps.cache.outputs.cache-hit != 'true'
+ run: |
+ pip3 install aqtinstall>=2.0.0
+ ./tools/ci_install_qt.sh mac ${QT_VERSION} clang_64 ${HOME}/Cache/Qt
+ echo "export PATH=${HOME}/Cache/Qt/${QT_VERSION}/macos/bin:\$PATH" > "${HOME}/Cache/qt-${QT_VERSION}.env"
+
+ - name: Script
+ run: |
+ source ${HOME}/Cache/qt-${QT_VERSION}.env
+ sudo xcode-select --switch /Applications/Xcode_12.5.1.app
+ ./tools/travis_script_osx
+
+ - name: 'Upload Artifacts'
+ uses: actions/upload-artifact@v2
+ with:
+ name: MacOS_Installer_Qt6
+ path: gui/GPSBabel-*.dmg
+ retention-days: 14
COMPILER: 'msvc2017'
RELEASE: false
FLOW: 'nmake'
- - QT_VERSION: '5.12.10'
- ARCH: 'x86'
- HOST_ARCH: 'x86'
- COMPILER: 'msvc2017'
+ - QT_VERSION: '6.2.0'
+ ARCH: 'amd64'
+ HOST_ARCH: 'amd64'
+ COMPILER: 'msvc2019_64'
+ AQTARCH: 'win64_msvc2019_64'
RELEASE: false
- FLOW: 'msbuild'
+ FLOW: 'nmake'
steps:
- name: Checkout repository
- name: Cache Qt
uses: actions/cache@v2
+ id: cache
with:
path: ~/Cache
key: ${{ runner.os }}-${{ matrix.QT_VERSION }}-${{ matrix.COMPILER }}-${{ secrets.CACHE_VERSION }}
+ - name: Install Qt setup(aqt)
+ if: ${{ (steps.cache.outputs.cache-hit != 'true') && startsWith(matrix.QT_VERSION, '6') }}
+ uses: actions/setup-python@v2
+ with:
+ python-version: '3.9'
+
+ - name: Install Qt(aqt)
+ if: ${{ (steps.cache.outputs.cache-hit != 'true') && startsWith(matrix.QT_VERSION, '6') }}
+ shell: bash
+ run: |
+ pip3 install aqtinstall>=2.0.0
+ ./tools/ci_install_qt.sh windows ${{ matrix.QT_VERSION }} ${{ matrix.AQTARCH }} ${HOME}/Cache/Qt
+
- name: Install Qt
+ if: ${{ (steps.cache.outputs.cache-hit != 'true') && !startsWith(matrix.QT_VERSION, '6') }}
env:
CI_BUILD_DIR: ${{ github.workspace }}
shell: bash
# Find includes in corresponding build directories
set(CMAKE_INCLUDE_CURRENT_DIR ON)
-# Find the Qt5Core library
-find_package(Qt5 COMPONENTS Core REQUIRED)
-if(${Qt5Core_VERSION} VERSION_LESS 5.12)
- message(FATAL_ERROR "Qt version ${Qt5Core_VERSION} found, but version 5.9 or newer is required.")
+# Find the QtCore library
+find_package(QT NAMES Qt6 Qt5 COMPONENTS Core REQUIRED)
+find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core REQUIRED)
+list(APPEND QT_LIBRARIES Qt${QT_VERSION_MAJOR}::Core)
+if(${Qt${QT_VERSION_MAJOR}Core_VERSION} VERSION_LESS 5.12)
+ message(FATAL_ERROR "Qt version ${Qt${QT_VERSION_MAJOR}Core_VERSION} found, but version 5.12 or newer is required.")
else()
- message(STATUS "Using Qt5 version ${Qt5Core_VERSION}")
+ message(STATUS "Using Qt${QT_VERSION_MAJOR} version ${Qt${QT_VERSION_MAJOR}Core_VERSION}")
+endif()
+if(${QT_VERSION_MAJOR} EQUAL "6")
+ find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core5Compat REQUIRED)
+ list(APPEND QT_LIBRARIES Qt${QT_VERSION_MAJOR}::Core5Compat)
endif()
set(MINIMAL_FMTS
src/core/usasciicodec.cc
src/core/xmlstreamwriter.cc
)
+if(${QT_VERSION_MAJOR} EQUAL "6")
+ set(SUPPORT ${SUPPORT} src/core/codecdevice.cc)
+endif()
set(HEADERS
cet.h
src/core/xmlstreamwriter.h
src/core/xmltag.h
)
+if(${QT_VERSION_MAJOR} EQUAL "6")
+ set(HEADERS ${HEADERS} src/core/codecdevice.h)
+endif()
string(REPLACE .cc .h FILTER_HEADERS "${FILTERS}")
set(HEADERS ${HEADERS} ${FILTER_HEADERS})
add_definitions(-DCSVFMTS_ENABLED)
add_executable(gpsbabel ${SOURCES} ${HEADERS})
-target_link_libraries(gpsbabel Qt5::Core ${LIBS})
+target_link_libraries(gpsbabel ${QT_LIBRARIES} ${LIBS})
message(STATUS "Sources are: \"${SOURCES}\"")
message(STATUS "Headers are: \"${HEADERS}\"")
}
QT -= gui
+versionAtLeast(QT_VERSION, 6.0): QT += core5compat
TARGET = gpsbabel
VERSION = 1.7.0
src/core/usasciicodec.cc \
src/core/xmlstreamwriter.cc
+versionAtLeast(QT_VERSION, 6.0): SUPPORT += src/core/codecdevice.cc
+
HEADERS = \
cet.h \
cet_util.h \
src/core/xmlstreamwriter.h \
src/core/xmltag.h
+versionAtLeast(QT_VERSION, 6.0): HEADERS += src/core/codecdevice.h
+
HEADERS += $$FILTER_HEADERS
win32-msvc* {
#include <QDebug> // for QDebug
#include <QRegularExpression> // for QRegularExpression
#include <QString> // for QString, operator+
-#include <QStringRef> // for QStringRef
#include "defs.h"
#include "csv_util.h"
const int sp = p;
while (p < string.size() && !dfound) {
- if ((elen > 0) && string.midRef(p).startsWith(enclosed_in)) {
+ if ((elen > 0) && string.mid(p).startsWith(enclosed_in)) {
efound = true;
p += elen;
enclosed = !enclosed;
}
if (!enclosed) {
- if ((dlen > 0) && string.midRef(p).startsWith(delimiter)) {
+ if ((dlen > 0) && string.mid(p).startsWith(delimiter)) {
dfound = true;
} else if (hyper_whitespace_delimiter && string.at(p).isSpace()) {
dfound = true;
using QList<Waypoint*>::count; // a.k.a. size()
using QList<Waypoint*>::crbegin;
using QList<Waypoint*>::crend;
+ using QList<Waypoint*>::detach; // silence Qt6 foreach warnings
using QList<Waypoint*>::empty; // a.k.a. isEmpty()
using QList<Waypoint*>::end;
using QList<Waypoint*>::front; // a.k.a. first()
using QList<route_head*>::count; // a.k.a. size()
using QList<route_head*>::crbegin;
using QList<route_head*>::crend;
+ using QList<route_head*>::detach; // silence Qt6 foreach warnings
using QList<route_head*>::empty; // a.k.a. isEmpty()
using QList<route_head*>::end;
using QList<route_head*>::front; // a.k.a. first()
// In 95% of the callers, this could be s1.startsWith(s2)...
inline int case_ignore_strncmp(const QString& s1, const QString& s2, int n)
{
- return s1.leftRef(n).compare(s2.left(n), Qt::CaseInsensitive);
+ return s1.left(n).compare(s2.left(n), Qt::CaseInsensitive);
}
int str_match(const char* str, const char* match);
base = 7680;
}
if (base) {
- n = desc.midRef(7).toInt();
+ n = desc.mid(7).toInt();
return n + base;
}
}
# Handle the Qt rcc code generator automatically
set(CMAKE_AUTORCC ON)
-# Find the Qt5Core library
-find_package(Qt5 COMPONENTS Core Gui Network Widgets Xml REQUIRED)
-list(APPEND QT_LIBRARIES Qt5::Core Qt5::Gui Qt5::Network Qt5::Widgets Qt5::Xml)
-if(${Qt5Core_VERSION} VERSION_LESS 5.12)
- message(FATAL_ERROR "Qt version ${Qt5Core_VERSION} found, but version 5.9 or newer is required.")
+# Find the QtCore library
+find_package(QT NAMES Qt6 Qt5 COMPONENTS Core REQUIRED)
+find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Gui Network Widgets Xml REQUIRED)
+list(APPEND QT_LIBRARIES Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Gui Qt${QT_VERSION_MAJOR}::Network Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::Xml)
+if(${Qt${QT_VERSION_MAJOR}Core_VERSION} VERSION_LESS 5.12)
+ message(FATAL_ERROR "Qt version ${Qt${QT_VERSION_MAJOR}Core_VERSION} found, but version 5.12 or newer is required.")
else()
- message(STATUS "Using Qt5 version ${Qt5Core_VERSION}")
+ message(STATUS "Using Qt${QT_VERSION_MAJOR} version ${Qt${QT_VERSION_MAJOR}Core_VERSION}")
endif()
-# hard code webengine instead of webkit for now
-find_package(Qt5 COMPONENTS WebEngineWidgets WebChannel REQUIRED)
-list(APPEND QT_LIBRARIES Qt5::WebEngineWidgets Qt5::WebChannel)
+find_package(Qt${QT_VERSION_MAJOR} COMPONENTS WebEngineWidgets WebChannel REQUIRED)
+list(APPEND QT_LIBRARIES Qt${QT_VERSION_MAJOR}::WebEngineWidgets Qt${QT_VERSION_MAJOR}::WebChannel)
if(APPLE)
find_library(IOKIT_LIBRARIES IOKit)
# $Id: app.pro,v 1.19 2010-11-01 03:30:42 robertl Exp $
#
-CONFIG += qt
+#CONFIG += qt causes link failure on msvc. Qt6EntryPoint.lib not added to libs.
CONFIG(debug, debug|release) {
CONFIG += console
}
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+
+#include <cstdio> // for sscanf
+#include <cstdlib> // for atoi
+#include <ctime> // for strftime, localtime, time_t, tm
+
+#include <QByteArray> // for QByteArray
+#include <QIODevice> // for QIODevice, QIODeviceBase::ReadOnly
+#include <QList> // for QList
+#include <QString> // for QString, operator==
+#include <QXmlStreamAttributes> // for QXmlStreamAttributes
+#include <QtGlobal> // for QT_VERSION, QT_VERSION_CHECK, qPrintable
+
#include "defs.h"
-#include "xmlgeneric.h"
-#include <QXmlStreamAttributes>
-#include <cstdio>
+#include "gbfile.h" // for gbfprintf, gbfclose, gbfopen, gbfile
+#include "src/core/datetime.h" // for DateTime
+#include "src/core/file.h" // for File
+#include "xmlgeneric.h" // for xg_callback, xg_string, cb_cdata, xml_deinit, xml_init, xml_readunicode, cb_start, cb_end, xg_cb_type, xg_tag_mapping
+
#define MYNAME "IGNRando"
+#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
+static QString rd_fname;
+#endif
static gbfile* fout;
static route_head* track;
{
ignr_xml_error((wpt == nullptr) || (args.isEmpty()));
- if (2 != sscanf(CSTRc(args), "%lf,%lf", &wpt->latitude, &wpt->longitude)) {
+ if (2 != sscanf(STRFROMUNICODE(args), "%lf,%lf", &wpt->latitude, &wpt->longitude)) {
fatal(MYNAME ": Invalid coordinates \"%s\"!\n", qPrintable(args));
}
}
return;
}
- if (1 != sscanf(CSTRc(args), "%lf", &wpt->altitude)) {
+ if (1 != sscanf(STRFROMUNICODE(args), "%lf", &wpt->altitude)) {
fatal(MYNAME ": Invalid altitude \"%s\"!\n", qPrintable(args));
}
}
static void
ignr_rd_init(const QString& fname)
{
+#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
xml_init(fname, ignr_xml_map, nullptr);
+#else
+ rd_fname = fname;
+ xml_init(nullptr, ignr_xml_map, nullptr);
+#endif
wpt = nullptr;
track = nullptr;
}
ignr_rd_deinit()
{
xml_deinit();
+#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
+ rd_fname.clear();
+#endif
}
static void
ignr_read()
{
+#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
+ // QXmlStreamReader had access to the windows-1252 QTextCodec that we expect
+ // to find in the XMLDecl.
xml_read();
+#else
+ // QXmlStreamReader doesn't have access to a windows-1252 QStringDecoder,
+ // and will throw an error if we pass a QIODevice when it sees windows-1252
+ // in the XMLDecl.
+ // Therfore we must decode the input manually and pass a QString.
+ // With a QString QXmlStreamReader will ignore the XMLDecl.
+ gpsbabel::File file(rd_fname);
+ file.open(QIODevice::ReadOnly);
+ xml_readunicode(STRTOUNICODE(file.readAll()));
+ file.close();
+#endif
}
/* write support */
gbfprintf(fout, "\t<INFORMATIONS>\n");
gbfprintf(fout, "\t\t<NB_ETAPES>%d</NB_ETAPES>\n", track_hdr->rte_waypt_ct);
if (!track_hdr->rte_desc.isEmpty()) {
- gbfprintf(fout, "\t\t<DESCRIPTION>%s</DESCRIPTION>\n", CSTRc(track_hdr->rte_desc));
+ gbfprintf(fout, "\t\t<DESCRIPTION>%s</DESCRIPTION>\n", STRFROMUNICODE(track_hdr->rte_desc));
}
gbfprintf(fout, "\t</INFORMATIONS>\n");
}
gpsbabel::File file(name);
file.open(QFile::ReadOnly);
QTextStream stream(&file);
+#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
+ // default for QTextStream::setCodec in Qt5 is QTextCodec::codecForLocale()
+ // default for QTextStream::setEncoding in Qt6 is QStringConverter::Utf8
stream.setCodec("UTF-8");
+#endif
stream.setAutoDetectUnicode(true);
auto* result = new inifile_t;
* Also return the icon number for descriptions of "icon-"
* followed by a numeric icon number.
*/
- int n = desc.midRef(desc.startsWith("icon-") ? 5 : 0).toInt();
+ int n = desc.mid(desc.startsWith("icon-") ? 5 : 0).toInt();
if (n) {
return n;
}
static QDateTime maggeo_parsedate(char* dmy)
{
QString date(dmy);
- int d = date.midRef(0,2).toInt();
- int m = date.midRef(2,2).toInt();
- int y = date.midRef(4,3).toInt();
+ int d = date.mid(0,2).toInt();
+ int m = date.mid(2,2).toInt();
+ int y = date.mid(4,3).toInt();
+#if (QT_VERSION < QT_VERSION_CHECK(5, 14, 0))
QDateTime r(QDate(y + 1900, m, d));
+#else
+ QDateTime r = QDate(y + 1900, m, d).startOfDay();
+#endif
return r;
}
if (ok) {
int ckval = nmea_cksum(tbuf.mid(1, ckidx - 1));
if (ckval != ckcmp) {
- Warning().nospace() << hex << "Invalid NMEA checksum. Computed 0x" << ckval << " but found 0x" << ckcmp << ". Ignoring sentence.";
+ Warning().nospace() << Qt::hex << "Invalid NMEA checksum. Computed 0x" << ckval << " but found 0x" << ckcmp << ". Ignoring sentence.";
return;
}
checked = true;
wpt_tmp->longitude = lon;
wpt_tmp->latitude = lat;
} else {
- lat = tbuf.midRef(1, -1).toDouble();
- lon = nbuf.midRef(1, -1).toDouble();
+ lat = tbuf.mid(1, -1).toDouble();
+ lon = nbuf.mid(1, -1).toDouble();
if (tbuf[0] == 'S') {
lat = -lat;
}
wpt_tmp->longitude = lon;
wpt_tmp->latitude = lat;
} else {
- lat = tbuf.midRef(1, -1).toDouble();
- lon = nbuf.midRef(1, -1).toDouble();
+ lat = tbuf.mid(1, -1).toDouble();
+ lon = nbuf.mid(1, -1).toDouble();
if (tbuf[0] == 'S') {
lat = -lat;
}
#include <QByteArray> // for QByteArray
#include <QLatin1String> // for QLatin1String
-#include <QString> // for QString, QString::SkipEmptyParts
+#include <QString> // for QString, Qt::SkipEmptyParts
#include <QStringList> // for QStringList
#include <QVector> // for QVector
#include <Qt> // for CaseInsensitive
if (qopt_name.contains('+')) {
// form a compound name from one or more fields.
nameidx = -2;
+#if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0))
const QStringList opt_name_fields = qopt_name.split('+', QString::SkipEmptyParts);
+#else
+ const QStringList opt_name_fields = qopt_name.split('+', Qt::SkipEmptyParts);
+#endif
nameindices.reserve(opt_name_fields.size());
for (int oidx=0; oidx<opt_name_fields.size(); oidx++) {
bool ok;
--- /dev/null
+/*
+ Copyright (C) 2021 Robert Lipe, robertlipe+source@gpsbabel.org
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ */
+
+#include <cstring> // for memcpy
+#include <algorithm> // for min
+
+#include <QByteArray> // for QByteArray
+#include <QChar> // for QChar
+#include <QFile> // for QFile
+#include <QFlags> // for QFlags
+
+#include "defs.h" // for list_codecs, warning
+#include "codecdevice.h"
+
+namespace gpsbabel
+{
+
+CodecDevice::CodecDevice(const QString& fname, const char* module, const char* codec_name) :
+ fname_(fname), module_(module), codec_name_(codec_name)
+{
+}
+
+CodecDevice::~CodecDevice()
+{
+// close();
+}
+
+bool CodecDevice::open(QIODevice::OpenMode mode)
+{
+ codec_ = QTextCodec::codecForName(codec_name_);
+ if (codec_ == nullptr) {
+ list_codecs();
+ fatal("%s: Unsupported codec '%s'.\n", module_, codec_name_);
+ // return false;
+ }
+
+ if (mode & QIODevice::ReadOnly) {
+ decoder_ = codec_->makeDecoder();
+ }
+
+ if (mode & QIODevice::WriteOnly) {
+ encoder_ = codec_->makeEncoder();
+ }
+
+ file_ = new gpsbabel::File(fname_);
+ bool status = file_->open(mode);
+
+ QIODevice::open(mode);
+
+ return status;
+
+}
+
+qint64 CodecDevice::readData(char* data, qint64 maxlen)
+{
+ qint64 bytesdelivered = 0;
+
+ while (bytesdelivered < maxlen) {
+ if (unicodebuffer_bytes_ > 0) {
+ qint64 bytes = std::min(maxlen, unicodebuffer_bytes_);
+ memcpy(data, unicodebuffer_data_, bytes);
+ unicodebuffer_bytes_ -= bytes;
+ unicodebuffer_data_ += bytes;
+ data += bytes;
+ bytesdelivered += bytes;
+ if (bytesdelivered == maxlen) {
+ break;
+ }
+ }
+
+ qint64 bytesread = file_->read(charbuffer_, charbuffer_size_);
+ if (bytesread <= 0) { // no more data is available or error.
+ if (bytesdelivered > 0) {
+ break;
+ }
+ return -1;
+ }
+
+ unicodebuffer_ = decoder_->toUnicode(charbuffer_, bytesread);
+ unicodebuffer_bytes_ = unicodebuffer_.size() * sizeof(QChar);
+ unicodebuffer_data_ = reinterpret_cast<const char*>(unicodebuffer_.constData());
+ }
+ return bytesdelivered;
+}
+
+qint64 CodecDevice::writeData(const char* data, qint64 len)
+{
+ qint64 bytes_consumed = 0;
+
+ while (bytes_consumed < len) {
+ qint64 bytes= std::min(len - bytes_consumed, charbuffer_bytes_free_);
+ memcpy(charbuffer_data_, data, bytes);
+ bytes_consumed += bytes;
+ charbuffer_data_ += bytes;
+ charbuffer_bytes_free_ -= bytes;
+ data += bytes;
+
+ if (charbuffer_bytes_free_ == 0) {
+ static_assert(charbuffer_size_%sizeof(QChar) == 0);
+ QByteArray ba = encoder_->fromUnicode(reinterpret_cast<const QChar*>(charbuffer_), charbuffer_size_/sizeof(QChar));
+ file_->write(ba);
+ charbuffer_data_ = charbuffer_;
+ charbuffer_bytes_free_ = charbuffer_size_;
+ }
+ }
+ return len;
+}
+
+void CodecDevice::close()
+{
+ if (charbuffer_bytes_free_ < charbuffer_size_) {
+ qint64 bytes = charbuffer_size_ - charbuffer_bytes_free_;
+ assert(bytes%sizeof(QChar) == 0);
+ QByteArray ba = encoder_->fromUnicode(reinterpret_cast<const QChar*>(charbuffer_), bytes/sizeof(QChar));
+ file_->write(ba);
+ charbuffer_data_ = charbuffer_;
+ charbuffer_bytes_free_ = charbuffer_size_;
+ }
+ file_->close();
+ QIODevice::close();
+
+ if (file_ != nullptr) {
+ delete file_;
+ file_ = nullptr;
+ }
+ if (encoder_ != nullptr) {
+ delete encoder_;
+ encoder_ = nullptr;
+ }
+ if (decoder_ != nullptr) {
+ delete decoder_;
+ decoder_ = nullptr;
+ }
+}
+
+bool CodecDevice::isSequential() const
+{
+ return true;
+}
+
+} // namespace
--- /dev/null
+/*
+ Copyright (C) 2021 Robert Lipe, robertlipe+source@gpsbabel.org
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ */
+
+#include <QIODevice> // for QIODevice
+#include <QString> // for QString
+#include <QTextCodec> // for QTextCodec
+#include <QTextDecoder> // for QTextDecoder
+#include <QTextEncoder> // for QTextEncoder
+#include <QtCore> // for qint64, QIODeviceBase::OpenMode
+
+#include "src/core/file.h" // for File
+
+namespace gpsbabel
+{
+
+class CodecDevice : public QIODevice
+{
+public:
+ CodecDevice(const QString& fname, const char* module, const char* codec_name);
+ ~CodecDevice();
+ bool open(QIODevice::OpenMode mode) override;
+ bool isSequential() const override;
+ void close() override;
+
+private:
+ qint64 readData(char* data, qint64 maxlen) override;
+ qint64 writeData(const char* data, qint64 len) override;
+
+private:
+ QString fname_;
+ const char* module_;
+ const char* codec_name_;
+ gpsbabel::File* file_{nullptr};
+ QTextCodec* codec_{nullptr};
+ QTextDecoder* decoder_{nullptr};
+ QTextEncoder* encoder_{nullptr};
+ QString unicodebuffer_;
+ qint64 unicodebuffer_bytes_{0};
+ const char* unicodebuffer_data_{nullptr};
+ static constexpr qint64 charbuffer_size_ = 1024;
+ char charbuffer_[charbuffer_size_];
+ char* charbuffer_data_{charbuffer_};
+ qint64 charbuffer_bytes_free_{charbuffer_size_};
+};
+
+} // namespace
explicit FatalMsg() : QDebug(QtCriticalMsg) {}
};
+/*
+ * Kludge any used QTextStream modifiers into Qt namespace as they are in newer
+ * versions of Qt. This makes source compatiblity easier.
+ */
+#if (QT_VERSION < QT_VERSION_CHECK(5, 14, 0))
+namespace Qt
+{
+ inline QTextStream& hex(QTextStream &s) { return ::hex(s); }
+ inline QTextStream& endl(QTextStream &s) { return ::endl(s); }
+}
+#endif
#endif // gpsbabel_logging_h_included
/*
- Copyright (C) 2019 Robert Lipe, gpsbabel.org
+ Copyright (C) 2019-2021 Robert Lipe, gpsbabel.org
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
*/
-#include <QFile> // for QFile
+
+#include <QtCore> // for qint64, QT_VERSION, QT_VERSION_CHECK
+
+#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
+#include <QByteArrayView> // for QByteArrayView
+#endif
+#include <QFile> // for QFile
#include <QFlags> // for QFlags
-#include <QIODevice> // for QIODevice, QIODevice::OpenMode, QIODevice::ReadOnly, QIODevice::WriteOnly
+#include <QIODevice> // for QIODevice
+#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
+#include <QIODeviceBase> // for QIODeviceBase::OpenMode
+#include <QStringConverter> // for QStringConverter, QStringConverter::Utf8, QStringConverter::Encoding, QStringConverter::Utf16
+#endif
-#include "defs.h" // for fatal, list_codecs
+#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
+#include <optional> // for optional
+#endif
+
+#include "defs.h" // for fatal, list_codecs
#include "src/core/textstream.h"
-#include "src/core/file.h" // for File
+#include "src/core/file.h" // for File
namespace gpsbabel
void TextStream::open(const QString& fname, QIODevice::OpenMode mode, const char* module, const char* codec_name)
{
+#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
codec_ = QTextCodec::codecForName(codec_name);
if (codec_ == nullptr) {
list_codecs();
setGenerateByteOrderMark(true);
}
}
+#else
+ std::optional<QStringConverter::Encoding> encoding = QStringConverter::encodingForName(codec_name);
+ bool use_stringconverter = encoding.has_value();
+
+ /* When reading autodetect unicode.
+ * The requested codec may not be supported by QStringConverter,
+ * but autodetection may switch to a converter that is.
+ */
+ if (!use_stringconverter && (mode & QFile::ReadOnly)) {
+ QFile file = QFile(fname);
+ file.open(mode);
+ char data[4];
+ qint64 bytesread = file.read(data, 4);
+ file.close();
+ encoding = QStringConverter::encodingForData(QByteArrayView(data, bytesread));
+ if (encoding.has_value()) {
+ use_stringconverter = true;
+ }
+ }
+
+ if (use_stringconverter) {
+ file_ = new gpsbabel::File(fname);
+ file_->open(mode);
+ setDevice(file_);
+ setEncoding(encoding.value());
+
+ if (mode & QFile::ReadOnly) {
+ if (encoding.value() == QStringConverter::Utf8) {
+ setAutoDetectUnicode(true);
+ }
+ }
+
+ if (mode & QFile::WriteOnly) {
+ // enable bom for all UTF codecs except UTF-8
+ if (encoding.value() != QStringConverter::Utf8) {
+ setGenerateByteOrderMark(true);
+ }
+ }
+ } else {
+ device_ = new gpsbabel::CodecDevice(fname, module, codec_name);
+ bool status = device_->open(mode);
+ if (!status) {
+ fatal("%s: device not open %d\n", module, status);
+ }
+ setDevice(device_);
+ setEncoding(QStringConverter::Utf16);
+ }
+#endif
}
void TextStream::close()
delete file_;
file_ = nullptr;
}
+#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
codec_ = nullptr;
+#else
+ if (device_ != nullptr) {
+ device_->close();
+ delete device_;
+ device_ = nullptr;
+ }
+#endif
}
} // namespace
/*
- Copyright (C) 2019 Robert Lipe, gpsbabel.org
+ Copyright (C) 2019-2021 Robert Lipe, gpsbabel.org
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
#ifndef SRC_CORE_TEXTSTREAM_INCLUDED_H_
#define SRC_CORE_TEXTSTREAM_INCLUDED_H_
-#include <QIODevice> // for QIODevice, QIODevice::OpenMode
-#include <QString> // for QString
-#include <QTextCodec> // for QTextCodec
-#include <QTextStream> // for QTextStream
+#include <QtGlobal> // for QT_VERSION, QT_VERSION_CHECK
-#include "src/core/file.h" // for File
+#include <QIODevice> // for QIODevice
+#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
+#include <QIODeviceBase> // for QIODeviceBase::OpenMode
+#endif
+#include <QString> // for QString
+#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
+#include <QTextCodec> // for QTextCodec
+#endif
+#include <QTextStream> // for QTextStream
+
+#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
+#include "src/core/codecdevice.h" // for CodecDevice
+#endif
+#include "src/core/file.h" // for File
namespace gpsbabel
private:
gpsbabel::File* file_{nullptr};
+#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
QTextCodec* codec_{nullptr};
+#else
+ gpsbabel::CodecDevice* device_{nullptr};
+#endif
};
} // namespace
make -j 3
make check
-export CLAZY_CHECKS=level0,level1,no-non-pod-global-static
+export CLAZY_CHECKS=level0,level1,no-non-pod-global-static,no-qstring-ref
qmake -spec linux-clang "CONFIG+=debug" "QMAKE_CXX=clazy"
make clean
make -j 3 2>&1 | tee clazy.log
--- /dev/null
+#!/bin/bash -ex
+
+if [ $# -ne 1 ]; then
+ echo "$0 version"
+ exit 1
+fi
+version=${1}
+
+sourcetype=git
+#flavor=debug
+flavor=release
+#flavor=debug-and-release
+
+buildroot=$(pwd)/qt-${version}-${flavor}-${sourcetype}
+mkdir "$buildroot"
+sourcedir=${buildroot}/source
+builddir=${buildroot}/build
+if [ "$(uname -s)" = "Darwin" ]; then
+ installdir=/Users/travis/Cache/Qt
+ compilerdir=clang_64
+ archive=qt-${version}-${flavor}-macos
+else
+ installdir=/home/travis/Cache/Qt
+ compilerdir=gcc_64
+ archive=qt-${version}-${flavor}-linux
+fi
+
+
+if [ -e "${sourcedir}" ]; then
+ echo "source directory \"${sourcedir}\" already exits."
+ exit 1
+fi
+if [ -e "${builddir}" ]; then
+ echo "build directory \"${builddir}\" already exits."
+ exit 1
+fi
+if [ -e "${installdir}" ]; then
+ echo "install directory \"${installdir}\" already exits."
+ exit 1
+fi
+
+if [ "${sourcetype}" == "git" ]; then
+ git clone git://code.qt.io/qt/qt5.git "${sourcedir}"
+else
+ mkdir -p "${sourcedir}"
+ versionmm=$(echo "${version}" | cut -d. -f1,2)
+ if [ ! -e "qt-everywhere-src-${version}.tar.xz" ]; then
+ wget -nv "https://download.qt.io/archive/qt/${versionmm}/${version}/single/qt-everywhere-src-${version}.tar.xz"
+ fi
+ tar -x --strip-components 1 --xz --directory "${sourcedir}" --file "qt-everywhere-src-${version}.tar.xz"
+fi
+
+cd "${sourcedir}"
+
+# exclude modules without some kind of LGPL license.
+# virtualkeyboard is pretty sticky, it gets deployed if it exists and you use Gui.
+excludes=( \
+qtcharts \
+qtdatavis3d \
+qtlottie \
+qtnetworkauth \
+qtquick3d \
+qtvirtualkeyboard \
+qtwebglplugin \
+)
+
+# also some modules we don't use
+excludes+=( \
+qtpurchasing \
+qtscript \
+qtxmlpatterns \
+qtactiveqt \
+qtcoap \
+qtopcua \
+qtmqtt \
+qtscxml \
+)
+
+# other modules we don't want
+# the tarballs don't include these modules, but git does
+excludes+=( \
+qtqa \
+qtscript \
+qtquickcontrols2 \
+qtwayland \
+qt3d \
+qtquicktimeline \
+qtdoc \
+)
+#qtwebengine \
+#qtwebchannel \
+
+# qtmultimedia requires:
+#qtimageformats \
+#qtshadertools \
+
+if [ "${sourcetype}" == "git" ]; then
+ if true; then
+ # tagged when released
+ git checkout "v${version}"
+ else
+ # branch, before tagged
+ git checkout "${version}"
+ fi
+ modules=essential,addon,qt5compat,-$(echo "${excludes[@]}" | sed 's/ /,-/g')
+ echo "$modules"
+ perl init-repository --module-subset="${modules}"
+else
+ for component in "${excludes[@]}"
+ do
+ /bin/rm -fr "${component}"
+ done
+fi
+skips=$(echo "${excludes[@]}" | sed 's/[^ ]* */-skip &/g')
+mkdir -p "${builddir}"
+cd "${builddir}"
+"${sourcedir}/configure" --prefix="${installdir}/${version}/${compilerdir}" -opensource -confirm-license -nomake examples -nomake tests "-${flavor}" "${features[@]}" ${skips}
+cmake --build . --parallel
+cmake --install .
+
+licenses=( \
+"${sourcedir}/LICENSE.FDL" \
+"${sourcedir}/LICENSE.GPLv2" \
+"${sourcedir}/LICENSE.GPLv3" \
+"${sourcedir}/LICENSE.LGPLv21" \
+"${sourcedir}/LICENSE.LGPLv3" \
+"${sourcedir}/LICENSE.QT-LICENSE-AGREEMENT" \
+)
+
+mkdir "${installdir}/Licenses"
+for license in "${licenses[@]}"
+do
+ cp "${license}" "${installdir}/Licenses"
+done
+
+tar -c -C "$(dirname ${installdir})" --xz -f "${archive}.tar.xz" "$(basename ${installdir})"
--- /dev/null
+#!/bin/bash -e
+
+# install Qt
+#
+# Examples:
+# ci_install_qt.sh mac 6.2.0 clang_64 /tmp/Qt
+# ci_install_qt.sh windows 6.2.0 win64_msvc2019_64 /tmp/Qt
+# ci_install_qt.sh linux 6.2.0 gcc_64 /tmp/Qt
+
+host=$1
+version=$2
+arch=$3
+outdir=$4
+
+available=( $(aqt list-qt "$host" desktop --modules "$version" "$arch") )
+
+# remove commercial/GPLv3 modules, see https://doc-snapshots.qt.io/qt6-dev/qtmodules.html
+remove=( \
+debug_info \
+qtcharts \
+qtdatavis3d \
+qtlottie \
+qtnetworkauth \
+qtquick3d \
+qtquicktimeline \
+qtwebglplugin \
+qtshadertools \
+qtvirtualkeyboard \
+qtwaylandcompositor \
+)
+
+mods=()
+for a in "${available[@]}"
+do
+ skip=false
+ for r in "${remove[@]}"
+ do
+ if [ "$a" == "$r" ]; then
+ skip=true
+ fi
+ done
+ if [ $skip == false ]; then
+ mods+=( "$a" )
+ fi
+done
+echo Installing "${mods[@]}"
+aqt install-qt "$host" desktop "$version" "$arch" -O "$outdir" -m "${mods[@]}"
+
#
# this script is run on travis for the script stage of mac builds
#
+
+function version_ge() { test "$(printf "%s\n%s" $1 $2 | sort -rV | head -n 1)" == "$1"; }
QMAKE=${QMAKE:-qmake}
LUPDATE=${LUPDATE:-lupdate}
"$(cd "$(dirname "${BASH_SOURCE[0]}" )" && pwd)"/ci_tokens
# build and test the CLI
-$QMAKE GPSBabel.pro
+if version_ge $($QMAKE -query QT_VERSION) 6.0.0; then
+ $QMAKE GPSBabel.pro QMAKE_APPLE_DEVICE_ARCHS="x86_64 arm64"
+else
+ $QMAKE GPSBabel.pro
+fi
make -j 3
make check
# build the GUI
pushd gui
-$QMAKE app.pro
+if version_ge $($QMAKE -query QT_VERSION) 6.0.0; then
+ $QMAKE app.pro QMAKE_APPLE_DEVICE_ARCHS="x86_64 arm64"
+else
+ $QMAKE app.pro
+fi
make -j 3
make package
popd
maxlen = codec->name().size();
}
}
- info << "Available Codecs:" << endl;
- info << qSetFieldWidth(8) << "MIBenum" << qSetFieldWidth(maxlen+1) << "Name" << qSetFieldWidth(0) << "Aliases" << endl;
+ info << "Available Codecs:" << Qt::endl;
+ info << qSetFieldWidth(8) << "MIBenum" << qSetFieldWidth(maxlen+1) << "Name" << qSetFieldWidth(0) << "Aliases" << Qt::endl;
for (auto mib : mibs) {
auto* codec = QTextCodec::codecForMib(mib);
info << qSetFieldWidth(8) << mib << qSetFieldWidth(maxlen+1) << codec->name() << qSetFieldWidth(0);
}
info << alias;
}
- info << endl;
+ info << Qt::endl;
}
}
XcsvFormat::yyyymmdd_to_time(const char* s)
{
QDate d = QDate::fromString(s, "yyyyMMdd");
+#if (QT_VERSION < QT_VERSION_CHECK(5, 14, 0))
return QDateTime(d);
+#else
+ return d.startOfDay();
+#endif
}